package tpbox
{
	import mx.collections.ArrayCollection;
	import mx.containers.Box;
	import mx.containers.HBox;
	import mx.controls.ComboBox;
	import mx.controls.Label;
	import mx.core.UIComponent;
	import mx.events.DropdownEvent;
	import mx.events.ListEvent;

	[Event(name="change",type="mx.events.ListEvent")]
	
	public class TemplateBox extends Box
	{
		[Bindable]
		public var queryString:String ="";
		
		private var _template:XML;
		private var _fetcher:Function;
		
		private static const ITEM_TYPE_TEXT:String = "text";
		private static const ITEM_TYPE_SELECT:String = "combobox";
		private static const DEFAULT_WIDTH:int = 120;
		private static const DEFAULT_HEIGH:int = 22;
		
		private static const TAG_QUERY_CONTEX:String = "?queriedcontext";
		private static const TAG_SELECT:String = "select";
		
		private var counter:int;
		private var store:Array;
		private var lastGot:Object;
		
		[Event ]
		public function TemplateBox()
		{
			//TODO: implement function
			super();
			this.direction = "vertical";
			reset();
		}
		
		private function reset():void
		{
			this.removeAllChildren();
			store = new Array();
			counter = -1;
			lastGot = null;
		}
		/*
			<root>
				<line>
					<item>
						<type>text</type>
						<text>Select</text>
					</item>
					<item>
						<type>combobox</type>
						<value>defaultValue</value>
					</item>
					</item>
						<type>newline</type>
					</item>
				</line>
				...
			</root>
		*/
		private function render():void
		{
			reset();
			
			trace(" " + _template.toXMLString());
			
			for each( var line:XML in _template.line ){
				var box:HBox = createHBox();
				for each( var item:XML in line.item){
					if(item.type.toString() == ITEM_TYPE_TEXT){
						box.addChild(createLabel(item.text));
					}
					else if(item.type.toString() == ITEM_TYPE_SELECT){
						box.addChild(createComobox(item.value.toString()));
					}
				}
				box.addChild(createLabel("*\n*"));
				
				// ajust the width.
				for( var i:int = 0 ; i < box.getChildren().length ; i++){
					if( i < (box.getChildren().length - 1) ){
						box.getChildAt(i).width = (width - 40) / ( box.getChildren().length - 1);
					}
					else{
						box.getChildAt(i).width = 15;
					}
				}
				if( box.getChildren().length == 2 ){
					Label(box.getChildAt(0)).setStyle("textAlign","left");
				}
				addChild(box);
			}
			
			refreshQueryString();
		}
		private function createComobox(query:String):ComboBox
		{
			counter ++;
			
			var r:ComboBox = new ComboBox();
			r.text = query;
			r.dataProvider = new ArrayCollection(new Array());
			r.addEventListener(DropdownEvent.OPEN,dropHandler);
			r.addEventListener(ListEvent.CHANGE,changeHandler)
			r.editable = true;
			r.width = DEFAULT_WIDTH;
			r.height = DEFAULT_HEIGH;
			
			store[counter] = r;
			return r;
		}
		private function changeHandler(event:ListEvent):void
		{
			if(event.target != lastGot){
				lastGot = event.target;
			}
			refreshQueryString();
		}
		private function refreshQueryString():void
		{
			var ans:String = "";
			for( var i:int = 0 ; i < counter ; i++){
				var child:UIComponent = store[i];
				
				if( child is Label ){
					ans += Label(child).text;
				}
				else if( child is ComboBox){
					ans += ComboBox(child).text;
				}
			}
			
			queryString = ans;
			trace(" queryString " + queryString );
			dispatchEvent(new ListEvent("change"));
		}
		private function createLabel(text:String):Label
		{
			counter ++;
			
			var label:Label = new Label();
			label.text = reXML(text.substr(1,text.length-2));
			trace(' otext:"' + text + '" ntext:"' + label.text + '"');
			label.height = DEFAULT_HEIGH;
			label.setStyle("textAlign","center");
			label.y = 100;
			label.setStyle("fontWeight","bold");
			label.setStyle("fontSize","12");
			
			store[counter] = label;
			return label;
		}
		private static function reXML(s:String):String
		{
			s = s.replace("&amp;", "&");
			s = s.replace("&lt;", "<");
			s = s.replace("&gt;", ">");
			s = s.replace("&#x0A;", "\r"); // Safe - excessively safe
			s = s.replace("&#x0D;", "\n");
			return s;
		}
		private static function XMLize(s:String):String
		{
			trace(" before " + s);
//			s = s.replace("&", "&amp;");
//			s = s.replace("<", "&lt;");
//			s = s.replace(">", "&gt;");
//			s = s.replace("\r", "&#x0A;"); // Safe - excessively safe
//			s = s.replace("\n", "&#x0D;");
//			trace(" after " + s);
			return "* " + s + " *";
		}
		private function dropHandler(event:DropdownEvent):void
		{
			var child:ComboBox = event.target as ComboBox;
			
			if(child == lastGot)
				return;
				
			child.close();
			
			var query:String  = genQuery(child);
			
			var index:int = store.indexOf(child);
			
			trace(" index of child " + index ) ;
			
			var xml:XML = <queries>
							<query>{query}</query>
							<id>{index}</id>
						  	</queries>;	
						  	
			_fetcher.call(this,loadHandler,xml.toXMLString());		
		}
		
		/*
			<answer>
				<item>uir</item> 
				<item>uri</item>
				<id>
			</answer>
		*/
		private function loadHandler(xml:XML):void
		{
			trace(xml);
			
			var index:int = xml.id;
			
			var box:ComboBox = ComboBox(store[index]);
			
			var data:ArrayCollection = ArrayCollection(box.dataProvider);
			
			var array:Array = new Array();
			
			data.removeAll();
			//box.close();
						
			for each( var item:XML in xml.item){
				array.push(item.toString());
				data.addItem(item.toString());
			}
			
			box.open();
			
			lastGot = box;
			
			refreshQueryString();
			
			trace( array.length + " " + array[0] );
			trace( "index " + index);
			
		}
		
		private function genQuery(qchild:UIComponent):String
		{
			var ans:String = "";
			for( var i:int = 0 ; i < counter ; i++){
				var child:UIComponent = store[i];
				
				if( qchild == child){
					ans += TAG_QUERY_CONTEX;
				}else if( child is Label ){
					ans += Label(child).text;
				}
				else if( child is ComboBox){
					ans += ComboBox(child).text;
				}
			}
			
			var start:int = ans.toLocaleLowerCase().indexOf(TAG_SELECT) + len(TAG_SELECT);
			var end:int = ans.toLocaleLowerCase().indexOf("{",start);
			
			ans = ans.substring(0,start) + " " + TAG_QUERY_CONTEX + " where " + ans.substring(end-1,ans.length);
			
			trace(" ASK a query " + ans);
			
			return ans;
		}
		public static function canParse(query:String):Boolean
		{
			var q:String = query.toLocaleLowerCase();
			var selectStart:int = q.indexOf(TAG_SELECT);
			var selectEnd:int = selectStart + len(TAG_SELECT);
			
			if(selectStart == -1)
				return false;
			
			var braceStart:int = q.indexOf("{",selectEnd); 
			var braceEnd:int = q.indexOf("}",braceStart);
			if(braceStart == -1 || braceEnd == -1)
				return false;
			
			return true;
		}
		private static function transTriplesBody(body:String,xml:XML):void
		{
			var bodyList:Array = split(body);
			trace(" BodyList Size " + body.length);
			for( var i:int = 0 ; i < bodyList.length ; i+=3){
				var sub:String = bodyList[i];
				var pre:String = bodyList[i+1];
				var obj:String = bodyList[i+2];
				
				trace(" subject " + sub);
				trace("predicate " + pre);
				trace("object " + obj);
				
				var subXML:XML;
				var preXML:XML;
				var objXML:XML;
				
				subXML = transToXML(sub);
				objXML = transToXML(obj);
				preXML = <item>
							<type>text</type>
							<text>{ XMLize(pre)}</text>
						 </item>;
						 
				var lineXML:XML = <line>
									{subXML}
									{preXML}
									{objXML}
								  </line>;
				trace(" lineXML " + lineXML.toXMLString() );
				
				xml.appendChild(lineXML);
			}

		}
		private static function isAllTriples(body:String):Boolean
		{
			return false;
		}
		private static function transLiteral(literal:String,xml:XML):void
		{
			for each( var line:String in literal.split(/(\n|\r)/)){
				
				if(line == "|n" || line == "|r")
					continue;
					
				trace(" line "  + line);
				var r:XML = <line>
								<item>
									<type>text</type>
									<text>{XMLize(line)}</text>
								</item>
							</line>;
				xml.appendChild(r);
			}
		}
		private static function transBody(body:String,xml:XML):void
		{
			if( isAllTriples(body)){
				transTriplesBody(body,xml);
			}
			else
				transLiteral(body,xml);
		}
		public static function parse(query:String):XML
		{
			var q:String = query.toLocaleLowerCase();
			
			var selectStart:int = q.indexOf(TAG_SELECT);
			var selectEnd:int = selectStart + len(TAG_SELECT);
			
			var braceStart:int = q.indexOf("{",selectEnd); 
			var braceEnd:int = q.indexOf("}",braceStart);
			
			var head:String = query.substring(0,braceStart+1);
			var tail:String = query.substring(braceEnd,len(query));
			
			var body:String = query.substring(braceStart+1,braceEnd);
			
			trace("query " + query);
			trace("head " + head);
			trace("body " + body);
			trace("tail " + tail);
			
			
			
			var xml:XML = <root>
						  </root>;
						
			transLiteral(head,xml);
			
			transBody(body,xml);
			
			transLiteral(tail,xml);
			
			return xml;
		}

		private static function transToXML(sub:String):XML
		{
			/*var h:int = 0;
			for( var i:int = 0 ; i < sub.length ; i++){
				if( sub.charAt(i) != " " && sub.charAt(i) != "\t")
					break;
				h++;
			}*/
			if(sub.charAt(0) == "?" ){
				return <item>
							<type>text</type>
							<text>{XMLize(sub)}</text>
						 </item>
			}
			return <item>
							<type>combobox</type>
							<value>{sub}</value>
						 </item>;
		}
		private static function split(body:String):Array
		{
			var r:Array = new Array();
			var p:String = "";
			var f:Boolean = false;
			for(var i:int = 0 ;  i < body.length ; i++){
				var c:String = body.charAt(i);
				
				if( c == '"' )
					f = !f;
				
				if( (c == " " || c == " ." || c == "\r" || c == "\n" || c == "\t" ) && !f ){
					if( p.length > 0 ){
						r.push(p);
						p = "";
					}
//					if( c != "\n" && c != "\r")
//						p+= c;
					continue;
				}
				
				p+= c;				
			}
			if(p.length >0 )
				r.push(p);
			return r;
		}
		private static function len(s:String):int
		{
			return s.length;
		}
		private function createHBox():HBox
		{
			return new HBox() ;
		}
		
		public function set dataSource(templateString:XML):void
		{
			_template = templateString;
			render();
		}
		public function set fetcherFunction(func:Function):void
		{
			_fetcher = func;
		}
		
	}
}